package gate

import (
	"bytes"
	"clouddisk/internal/common"
	fpb "clouddisk/proto/file"
	upb "clouddisk/proto/user"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/rs/cors"
)

const maxUploadSize = 100 * 1024 * 1024

type GateServer struct {
	http.Server
	discovery *common.Discovery
	channels  *common.ServiceManager

	userService string
	fileService string
}

func WriteErrorResponse(w http.ResponseWriter, r *http.Request, status int, message string) {
	errResp := Response{
		Code:   int32(status),
		Errmsg: message,
		Data:   time.Now().GoString(),
	}

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("X-Request-Id", r.Header.Get("X-Request-Id"))
	w.WriteHeader(status)

	if err := json.NewEncoder(w).Encode(errResp); err != nil {
		log.Printf("ERROR: Json序列化失败: %v", err)
		http.Error(w, "内部服务器错误", http.StatusInternalServerError)
	}
}

func EnsurePost(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			WriteErrorResponse(w, r, http.StatusMethodNotAllowed, "只支持 POST 方法")
			return
		}

		next.ServeHTTP(w, r)
	})
}

func ContentTypeJSON(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			WriteErrorResponse(w, r, http.StatusUnsupportedMediaType, "无效的内容类型, Content-Type 需为 application/json")
			return
		}

		next.ServeHTTP(w, r)
	})
}

func (s *GateServer) PutSingleFile(w http.ResponseWriter, r *http.Request) {
	claims, ok := r.Context().Value("userClaims").(*CustomClaims)
	if !ok {
		http.Error(w, "Invalid user context", http.StatusInternalServerError)
		return
	}
	uid := claims.UserID

	r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)

	if err := r.ParseMultipartForm(maxUploadSize); err != nil {
		if err == io.EOF || err.Error() == "http: request body too large" {
			http.Error(w, fmt.Sprintf("File too large. Max size is %d bytes", maxUploadSize), http.StatusBadRequest)
		} else {
			http.Error(w, "Failed to parse form", http.StatusBadRequest)
		}
		log.Printf("Form parse error: %v", err)
		return
	}

	uploadPath := r.FormValue("dirPath")
	if uploadPath == "" {
		http.Error(w, "Missing upload path", http.StatusBadRequest)
		return
	}

	// 提取文件
	file, fileHeader, err := r.FormFile("file")
	if err != nil {
		http.Error(w, "Failed to retrieve file", http.StatusBadRequest)
		log.Printf("File retrieval error: %v", err)
		return
	}
	defer file.Close()

	fileBytes, err := io.ReadAll(file)
	if err != nil {
		log.Printf("获取文件失败: %v", err)
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	conn := s.channels.Choose(s.fileService)
	if conn == nil {
		log.Println("获取rpc连接失败")
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	c := fpb.NewFileServiceClient(conn)
	req := &fpb.PutSingleFileReq{
		RequestId:   common.GenerateRequestID(),
		UserId:      uid,
		FilePath:    uploadPath,
		FileName:    fileHeader.Filename,
		FileContent: fileBytes,
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	rsp, err := c.PutSingleFile(ctx, req)
	if err != nil {
		log.Printf("文件上传出现错误: %v", err)
		WriteErrorResponse(w, r, http.StatusInternalServerError, "")
		return
	}

	hrsp := &Response{
		Code:   rsp.Code,
		Errmsg: rsp.Errmsg,
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(hrsp)
	if err != nil {
		log.Printf("序列化失败: %v", err)
		fmt.Fprintf(w, `{"error": "response serialization failed"}`)
		return
	}
}

func setDownloadHeaders(w http.ResponseWriter, size int) {
	// 设置内容类型
	w.Header().Set("Content-Type", "application/octet-stream")

	// 设置文件大小
	w.Header().Set("Content-Length", fmt.Sprintf("%d", size))

	// 支持断点续传
	w.Header().Set("Accept-Ranges", "bytes")

	// 浏览器缓存控制
	w.Header().Set("Cache-Control", "private, max-age=600")
}

func parseRangeHeader(headers http.Header, size int64) (start, end int64) {
	rangeHeader := headers.Get("Range")
	if rangeHeader == "" {
		return -1, -1 // 表示没有范围请求
	}

	// 尝试解析格式: bytes=0-99
	if strings.HasPrefix(rangeHeader, "bytes=") {
		rangeStr := rangeHeader[6:]
		parts := strings.Split(rangeStr, "-")
		if len(parts) < 2 {
			return -1, -1
		}

		start, err1 := strconv.ParseInt(parts[0], 10, 64)
		end, err2 := strconv.ParseInt(parts[1], 10, 64)

		if err1 != nil || start < 0 || start >= size {
			return -1, -1
		}

		if err2 != nil || end < start || end >= size {
			end = size - 1
		}

		return start, end
	}

	return -1, -1
}

func (s *GateServer) GetSingleFile(w http.ResponseWriter, r *http.Request) {
	claims, ok := r.Context().Value("userClaims").(*CustomClaims)
	if !ok {
		http.Error(w, "Invalid user context", http.StatusInternalServerError)
		return
	}
	uid := claims.UserID

	conn := s.channels.Choose(s.fileService)
	if conn == nil {
		log.Println("获取rpc连接失败")
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	c := fpb.NewFileServiceClient(conn)
	req := &fpb.GetSingleFileReq{
		RequestId: common.GenerateRequestID(),
		UserId:    uid,
		FilePath:  r.URL.Path,
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	rsp, err := c.GetSingleFile(ctx, req)
	if err != nil {
		log.Printf("文件获取出现错误: %v", err)
		WriteErrorResponse(w, r, http.StatusInternalServerError, "")
		return
	}

	if rsp.Code != 2 {
		WriteErrorResponse(w, r, http.StatusInternalServerError, rsp.Errmsg)
		return
	}

	pos := strings.LastIndex(r.URL.Path, "/")
	fileName := r.URL.Path[pos+1:]
	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName))

	// 返回文件数据
	size := len(rsp.FileContent)
	setDownloadHeaders(w, size)
	reader := bytes.NewReader(rsp.FileContent)
	start, end := parseRangeHeader(r.Header, int64(size))

	if start >= 0 {
		// 部分内容传输
		if _, err := reader.Seek(start, io.SeekStart); err != nil {
			http.Error(w, "Error seeking file", http.StatusInternalServerError)
			return
		}

		// 计算要传输的数据量
		partSize := end - start + 1
		w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, size))
		w.Header().Set("Content-Length", strconv.Itoa(int(partSize)))
		w.WriteHeader(http.StatusPartialContent)

		// 复制部分内容
		if _, err := io.CopyN(w, reader, partSize); err != nil {
			log.Printf("Error writing partial content: %v", err)
		}
	} else {
		// 完整文件传输
		if _, err := io.Copy(w, reader); err != nil {
			log.Printf("Error writing response: %v", err)
		}
	}
}

func (s *GateServer) GetUserFileList(w http.ResponseWriter, r *http.Request) {
	claims, ok := r.Context().Value("userClaims").(*CustomClaims)
	if !ok {
		http.Error(w, "Invalid user context", http.StatusInternalServerError)
		return
	}
	uid := claims.UserID

	var req fpb.GetUserFileListReq
	err := common.ParseJSONRequest(r, &req)
	if err != nil {
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	conn := s.channels.Choose(s.fileService)
	if conn == nil {
		log.Println("获取rpc连接失败")
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	c := fpb.NewFileServiceClient(conn)
	req.RequestId = common.GenerateRequestID()
	req.UserId = uid

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	rsp, err := c.GetUserFileList(ctx, &req)
	if err != nil {
		log.Printf("获取文件列表出现错误: %v", err)
		WriteErrorResponse(w, r, http.StatusInternalServerError, "")
		return
	}

	if rsp.Code != 2 {
		WriteErrorResponse(w, r, http.StatusInternalServerError, rsp.Errmsg)
		return
	}

	w.WriteHeader(http.StatusOK)
	err = common.WriteProtoJSON(w, rsp)
	if err != nil {
		log.Printf("序列化失败: %v", err)
		fmt.Fprintf(w, `{"error": "response serialization failed"}`)
		return
	}
}

func (s *GateServer) RenameFile(w http.ResponseWriter, r *http.Request) {
	claims, ok := r.Context().Value("userClaims").(*CustomClaims)
	if !ok {
		http.Error(w, "Invalid user context", http.StatusInternalServerError)
		return
	}
	uid := claims.UserID

	var req fpb.FileRenameReq
	err := common.ParseJSONRequest(r, &req)
	if err != nil {
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	conn := s.channels.Choose(s.fileService)
	if conn == nil {
		log.Println("获取rpc连接失败")
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	c := fpb.NewFileServiceClient(conn)
	req.RequestId = common.GenerateRequestID()
	req.UserId = uid

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	rsp, err := c.FileRename(ctx, &req)
	if err != nil {
		log.Printf("文件重命名失败: %v", err)
		WriteErrorResponse(w, r, http.StatusInternalServerError, "")
		return
	}

	if rsp.Code != 2 {
		WriteErrorResponse(w, r, http.StatusInternalServerError, rsp.Errmsg)
		return
	}

	w.WriteHeader(http.StatusOK)
	err = common.WriteProtoJSON(w, rsp)
	if err != nil {
		log.Printf("序列化失败: %v", err)
		fmt.Fprintf(w, `{"error": "response serialization failed"}`)
		return
	}
}

func (s *GateServer) DeleteFile(w http.ResponseWriter, r *http.Request) {
	claims, ok := r.Context().Value("userClaims").(*CustomClaims)
	if !ok {
		http.Error(w, "Invalid user context", http.StatusInternalServerError)
		return
	}
	uid := claims.UserID

	var req fpb.FileDelteReq
	err := common.ParseJSONRequest(r, &req)
	if err != nil {
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	conn := s.channels.Choose(s.fileService)
	if conn == nil {
		log.Println("获取rpc连接失败")
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	c := fpb.NewFileServiceClient(conn)
	req.RequestId = common.GenerateRequestID()
	req.UserId = uid

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	rsp, err := c.FileDelete(ctx, &req)
	if err != nil {
		log.Printf("删除文件失败: %v", err)
		WriteErrorResponse(w, r, http.StatusInternalServerError, "")
		return
	}

	if rsp.Code != 2 {
		WriteErrorResponse(w, r, http.StatusInternalServerError, rsp.Errmsg)
		return
	}

	w.WriteHeader(http.StatusOK)
	err = common.WriteProtoJSON(w, rsp)
	if err != nil {
		log.Printf("序列化失败: %v", err)
		fmt.Fprintf(w, `{"error": "response serialization failed"}`)
		return
	}
}

func (s *GateServer) UserLogin(w http.ResponseWriter, r *http.Request) {
	var accountInfo AccountInfo
	err := common.ParseJSONRequest(r, &accountInfo)
	if err != nil {
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	// rpc转发
	conn := s.channels.Choose(s.userService)
	if conn == nil {
		log.Println("获取rpc连接失败")
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	c := upb.NewUserServiceClient(conn)
	req := &upb.UserLoginReq{
		RequestId: common.GenerateRequestID(),
		Nickname:  accountInfo.UserName,
		Password:  accountInfo.Password,
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	rsp, err := c.UserLogin(ctx, req)
	if err != nil {
		log.Printf("用户登录出现错误: %v", err)
		WriteErrorResponse(w, r, http.StatusInternalServerError, "")
		return
	}

	// 创建jwt令牌
	tokenString, expiration, err := generateJWT(rsp.UserId, "user")
	if err != nil {
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}
	cookie := &http.Cookie{
		Name:     "token",
		Value:    tokenString,
		Expires:  expiration,
		Path:     "/",
		Secure:   true,
		SameSite: http.SameSiteStrictMode,
	}
	http.SetCookie(w, cookie)

	// 返回结果
	hrsp := &Response{
		Code:   rsp.Code,
		Errmsg: rsp.Errmsg,
		Data:   tokenString,
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(hrsp)
	if err != nil {
		log.Printf("序列化失败: %v", err)
		fmt.Fprintf(w, `{"error": "response serialization failed"}`)
		return
	}
}

func (s *GateServer) UserRegister(w http.ResponseWriter, r *http.Request) {
	var accountInfo AccountInfo
	err := common.ParseJSONRequest(r, &accountInfo)
	if err != nil {
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	// rpc转发
	conn := s.channels.Choose(s.userService)
	if conn == nil {
		log.Println("获取rpc连接失败")
		WriteErrorResponse(w, r, http.StatusServiceUnavailable, "")
		return
	}

	c := upb.NewUserServiceClient(conn)
	req := &upb.UserRegisterReq{
		RequestId: common.GenerateRequestID(),
		Nickname:  accountInfo.UserName,
		Password:  accountInfo.Password,
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	rsp, err := c.UserRegister(ctx, req)
	if err != nil {
		log.Printf("用户注册出现错误: %v", err)
		WriteErrorResponse(w, r, http.StatusInternalServerError, "")
		return
	}

	// 返回结果
	hrsp := &Response{
		Code:   rsp.Code,
		Errmsg: rsp.Errmsg,
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	err = json.NewEncoder(w).Encode(hrsp)
	if err != nil {
		log.Printf("序列化失败: %v", err)
		fmt.Fprintf(w, `{"error": "response serialization failed"}`)
		return
	}
}

func (s *GateServer) Init() http.Handler {
	mux := http.NewServeMux()

	c := cors.New(cors.Options{
		AllowedOrigins: []string{"*"},
		AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		AllowedHeaders: []string{"*"},
		ExposedHeaders: []string{"*"},
		MaxAge:         86400,
	})

	mux.Handle("/register", EnsurePost(ContentTypeJSON(http.HandlerFunc(s.UserRegister))))
	mux.Handle("/login", EnsurePost(ContentTypeJSON(http.HandlerFunc(s.UserLogin))))
	mux.Handle("/upload", AuthMiddleware(EnsurePost(http.HandlerFunc(s.PutSingleFile))))
	mux.Handle("/list", AuthMiddleware(EnsurePost(ContentTypeJSON(http.HandlerFunc(s.GetUserFileList)))))
	mux.Handle("/rename", AuthMiddleware(EnsurePost(ContentTypeJSON(http.HandlerFunc(s.RenameFile)))))
	mux.Handle("/delete", AuthMiddleware(EnsurePost(ContentTypeJSON(http.HandlerFunc(s.DeleteFile)))))

	downloadHandler := http.StripPrefix("/download/", http.HandlerFunc(s.GetSingleFile))
	mux.Handle("/download/", AuthMiddleware(downloadHandler))

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "OPTIONS" {
			w.WriteHeader(http.StatusNoContent)
			return
		}
		http.NotFound(w, r)
	})

	return c.Handler(mux)
}

func (s *GateServer) Serve(port string) error {
	handler := s.Init()

	server := &http.Server{
		Addr:         port,
		Handler:      handler,
		ReadTimeout:  15 * time.Second,
		WriteTimeout: 30 * time.Second,
		IdleTimeout:  60 * time.Second,
	}

	stopChan := make(chan os.Signal, 1)
	signal.Notify(stopChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)

	go func() {
		log.Printf("GateServer启动: %s", port)
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("服务器运行失败: %v", err)
			stopChan <- syscall.SIGTERM
		}
	}()

	<-stopChan
	log.Printf("开始平滑退出")

	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()

	if err := server.Shutdown(ctx); err != nil {
		log.Printf("服务器关闭失败: %v", err)
		return fmt.Errorf("服务器关闭失败: %w", err)
	}

	return nil
}

type GateServerBuilder struct {
	discovery   *common.Discovery
	channels    *common.ServiceManager
	userService string
	fileService string
	err         error
}

func NewGateServerBuilder() *GateServerBuilder {
	return &GateServerBuilder{}
}

func (gb *GateServerBuilder) WithServiceName(userService, fileService string) *GateServerBuilder {
	if gb.err != nil {
		return gb
	}

	gb.userService = userService
	gb.fileService = fileService

	return gb
}

func (gb *GateServerBuilder) WithSecretKey(secretKey string) *GateServerBuilder {
	if gb.err != nil {
		return gb
	}

	InitSecretKey(secretKey)
	return gb
}

func (gb *GateServerBuilder) WithDiscoverObject(host, baseService string) *GateServerBuilder {
	if gb.err != nil {
		return gb
	}

	if gb.userService == "" || gb.fileService == "" {
		gb.err = fmt.Errorf("未设置服务名称")
		return gb
	}

	gb.channels = common.NewServiceManager()
	gb.userService = baseService + gb.userService
	gb.fileService = baseService + gb.fileService
	gb.channels.Declare(gb.userService)
	gb.channels.Declare(gb.fileService)
	gb.discovery, gb.err = common.NewDiscovery(host, baseService, gb.channels.AServiceOnline, gb.channels.AServiceOffline)

	return gb
}

func (gb *GateServerBuilder) Build() (*GateServer, error) {
	if gb.err != nil {
		return nil, gb.err
	}

	if gb.discovery == nil {
		gb.err = fmt.Errorf("discovery未完成初始化")
		return nil, gb.err
	}

	gb.discovery.Watch()

	return &GateServer{
		discovery:   gb.discovery,
		channels:    gb.channels,
		userService: gb.userService,
		fileService: gb.fileService,
	}, nil
}
